/*
jQuery HTTP wrapper
支持的方法：GET, POST, PUT, DELETE
文件名：jQuery-http-wrapper.js
用法：在引入 jQuery 后导入或包含此文件

功能特性：
- 基于 Promise（返回原生 Promise）
- 自动解析/序列化 JSON 数据
- 自动处理 FormData 类型数据
- 支持默认请求头和单次调用设置请求头
- 超时控制、重试机制与指数退避策略
- 提供钩子函数：beforeSend, onSuccess, onError, onComplete
- 快捷方法：get, post, put, delete
*/

// 使用立即执行函数包装，防止全局污染，并传入 window 和 jQuery 对象
; (function (global, $) {
  // 检查是否加载了 jQuery，如果没有则抛出错误
  if (!$) throw new Error('jQuery is required for this wrapper')

  // 默认配置选项
  const defaultOptions = {
    baseURL: '',                         // 基础 URL
    headers: {                           // 默认请求头
      'Content-Type': 'application/json; charset=utf-8',
      Accept: 'application/json, text/plain, */*'
    },
    timeout: 30000,                      // 请求超时时间（毫秒）
    retries: 0,                          // 最大重试次数
    retryDelay: 500,                     // 初始重试延迟（毫秒）
    processData: true,                   // 是否自动处理发送的数据
    dataType: 'json',                    // 预期服务器响应的数据类型
    cache: false                         // 是否缓存请求结果
  }

  /**
   * 合并用户配置与默认配置
   * @param {Object} user 用户自定义配置
   * @return {Object} 合并后的配置对象
   */
  function mergeOpts (user) {
    return $.extend(true, {}, defaultOptions, user || {})
  }

  /**
   * 构建完整的请求 URL
   * @param {String} baseURL 基础地址
   * @param {String} url 相对路径
   * @return {String} 完整 URL
   */
  function buildUrl (baseURL, url) {
    if (!url) return baseURL || ''
    if (!baseURL) return url
    // 简单拼接两个 URL 并去除重复斜杠
    return (baseURL.replace(/\/+$/, '') + '/' + url.replace(/^\/+/, ''))
  }

  /**
   * 判断数据是否为 FormData 实例
   * @param {*} v 待判断值
   * @return {Boolean}
   */
  function isFormData (v) {
    return typeof FormData !== 'undefined' && v instanceof FormData
  }

  /**
   * 发起一个 HTTP 请求
   * @param {Object} opts 请求参数配置
   * @return {Promise} 返回一个 Promise 对象
   */
  function request (opts) {
    const o = mergeOpts(opts)
    o.url = buildUrl(o.baseURL, o.url)

    // 如果数据不是 FormData 且 Content-Type 是 application/json，则尝试将其转换为字符串
    if (o.data && !isFormData(o.data) && /application\/json/.test((o.headers['Content-Type'] || ''))) {
      try {
        o.data = typeof o.data === 'string' ? o.data : JSON.stringify(o.data)
      } catch (e) {
        // 出错时不改变原始数据
      }
    }

    // 如果是 FormData，则关闭 jQuery 的自动处理机制
    if (isFormData(o.data)) {
      o.processData = false
      o.contentType = false
    }

    let attempts = 0

    /**
     * 尝试发起 AJAX 请求的核心逻辑
     * @param {Function} resolve Promise 成功回调
     * @param {Function} reject Promise 失败回调
     */
    function attemptResolve (resolve, reject) {
      attempts++
      const ajaxSettings = {
        url: o.url,
        method: (o.method || 'GET').toUpperCase(),  // 请求方法，默认 GET
        data: o.data,                               // 请求体数据
        dataType: o.dataType,                       // 预期响应数据类型
        headers: o.headers,                         // 请求头
        timeout: o.timeout,                         // 超时时间
        processData: typeof o.processData === 'boolean' ? o.processData : defaultOptions.processData,
        contentType: typeof o.contentType !== 'undefined' ? o.contentType : o.headers['Content-Type'],
        cache: typeof o.cache === 'boolean' ? o.cache : defaultOptions.cache,
        beforeSend: function (jqXhr) {
          if (typeof o.beforeSend === 'function') {
            try { o.beforeSend(jqXhr) } catch (e) { /* 忽略错误 */ }
          }
        }
      }

      $.ajax(ajaxSettings)
        .done(function (resp, textStatus, jqXHR) {
          // 成功时触发 onSuccess 回调（如果有）
          if (typeof o.onSuccess === 'function') {
            try { o.onSuccess(resp, jqXHR) } catch (e) { /* 忽略错误 */ }
          }
          resolve({ data: resp, xhr: jqXHR, statusText: textStatus })
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
          // 判断是否应该重试
          const shouldRetry = attempts <= (o.retries || 0)
          if (shouldRetry) {
            // 计算当前应等待的时间（指数退避）
            const delay = (o.retryDelay || 0) * Math.pow(2, attempts - 1)
            setTimeout(() => attemptResolve(resolve, reject), delay)
            return
          }

          // 解析响应中的错误信息
          let parsed = null
          try {
            parsed = jqXHR && jqXHR.responseJSON ? jqXHR.responseJSON : jqXHR && jqXHR.responseText ? JSON.parse(jqXHR.responseText) : null
          } catch (e) {
            parsed = jqXHR.responseText
          }

          // 触发 onError 回调（如果有）
          if (typeof o.onError === 'function') {
            try { o.onError({ xhr: jqXHR, statusText: textStatus, error: errorThrown, body: parsed }) } catch (e) { /* 忽略错误 */ }
          }

          reject({ xhr: jqXHR, statusText: textStatus, error: errorThrown, body: parsed })
        })
        .always(function (a, b, c) {
          // 总是会执行的回调
          if (typeof o.onComplete === 'function') {
            try { o.onComplete(a, b, c) } catch (e) { /* 忽略错误 */ }
          }
        })
    }

    // 返回一个新的 Promise，在其中启动首次请求尝试
    return new Promise(function (resolve, reject) {
      attemptResolve(resolve, reject)
    })
  }

  // 添加便捷方法：get, post, put, delete
  request.get = function (url, opts) {
    return request($.extend({}, opts, { method: 'GET', url }))
  }
  request.post = function (url, data, opts) {
    return request($.extend({}, opts, { method: 'POST', url, data }))
  }
  request.put = function (url, data, opts) {
    return request($.extend({}, opts, { method: 'PUT', url, data }))
  }
  request.delete = function (url, data, opts) {
    return request($.extend({}, opts, { method: 'DELETE', url, data }))
  }

  // 将封装好的 request 方法挂载到全局对象上
  global.$http = request

})(window, window.jQuery);